package http

import (
	"context"
	"crypto/tls"
	"errors"
	"gitee.com/llakcs/agile-go/log"
	"github.com/gin-gonic/gin"
	"net"
	"net/http"
	"time"
)

// Server is an HTTP server wrapper.
type Server struct {
	*http.Server
	address string
	tls     *tls.Config
	timeout time.Duration
	router  *gin.Engine
}

type ServerOption func(*Server)

func WithServerAddress(addr string) ServerOption {
	return func(s *Server) {
		s.address = addr
	}
}

func WithServerTimeout(timeout time.Duration) ServerOption {
	return func(s *Server) {
		s.timeout = timeout
	}
}

func WithServerTLSConfig(t *tls.Config) ServerOption {
	return func(o *Server) {
		o.tls = t
	}
}

/**
 * 路由处理器
 */
func WithRouter(g *gin.Engine) ServerOption {
	return func(o *Server) {
		o.router = g
	}
}

// 新建http server
func NewServer(opts ...ServerOption) *Server {
	srv := &Server{
		Server:  nil,
		address: ":8288",
		tls:     nil,
		timeout: 5 * time.Second,
	}

	for _, o := range opts {
		o(srv)
	}
	if srv.router == nil {
		srv.router = gin.Default()
	}
	//超时控制中间件
	srv.router.Use(ctxTimeOutFilter(srv.timeout))
	server := &http.Server{
		Addr:      srv.address,
		Handler:   srv.router,
		TLSConfig: srv.tls,
	}
	srv.Server = server
	return srv
}

// 超时控制
func ctxTimeOutFilter(timeout time.Duration) gin.HandlerFunc {
	return func(c *gin.Context) {
		ctx, cancel := context.WithTimeout(c.Request.Context(), timeout)
		defer cancel()
		c.Request = c.Request.WithContext(ctx)
		c.Next()
	}
}

// start the HTTP server.
func (s *Server) Start(ctx context.Context) error {
	s.Server.BaseContext = func(listener net.Listener) context.Context {
		return ctx
	}
	log.Infof("[HTTP] server listening on: %s", s.Server.Addr)
	var err error
	if s.tls != nil {
		err = s.Server.ListenAndServeTLS("", "")
	} else {
		err = s.Server.ListenAndServe()
	}
	if !errors.Is(err, http.ErrServerClosed) {
		return err
	}
	return nil
}

// Stop stop the HTTP server.
func (s *Server) Stop(ctx context.Context) error {
	log.Info("[HTTP] server stopping")
	return s.Shutdown(ctx)
}
